;
$(function () {
  var getTemp = (function () {// cache template
    var cache = {};
    return function (name) {
      if (!cache[name]) {
        $.ajax({
          url: '/template/' + name,
          async: false,
          success: function (data) {
            cache[name] = data;
          }
        });
      }
      return cache[name] ? _.template(cache[name]) : function (e) {
        return e;
      };
    }
  })();

  var command = (function () {// command history
    function command() {
      this.cmd = [];
      this.index = 0
    }

    command.prototype.add = function (cmd) {
      this.cmd.push(cmd);
      this.index = this.cmd.length;
    }
    command.prototype.next = function () {
      this.index++;
      if (this.index > this.cmd.length - 1)
        this.index = this.cmd.length - 1;
      return this.cmd[this.index];
    }
    command.prototype.before = function () {
      this.index--;
      if (this.index < 0)
        this.index = 0;
      return this.cmd[this.index];
    }
    return command;
  })();

  var Reg = Backbone.Model.extend({
    urlRoot: '/reg'
  });
  var Login = Backbone.Model.extend({
    urlRoot: '/login'
  });
  var Favorite = Backbone.Model.extend({
    empty: function () {
      return !this.get('url');
    },
    defaults: {
      _token: simpleStorage.get('_token')
    },
    urlRoot: '/save',
    initialize: function () {
      _.bindAll(this, 'callback');
    },
    callback: function (callback) {
      return this.bind('sync', function (model, resp, options) {
        callback(model, resp, options);
      });
    }
  });
  var FavoriteCollection = Backbone.Collection.extend({
    model: Favorite,
    url: '/favorite',
    initialize: function () {
      _.bindAll(this, 'setCache', 'search', 'getMatch', 'restore');
      this.bind('sync', this.setCache);
    },
    setCache: function (datas) {// one collection's backup for search
      datas && datas.length ? this.cache = datas.slice(0) : this.cache.length = 0;
    },
    search: function (key) {
      var lowerKey = key.toLowerCase();
      var temp = _.filter(this.models, function (e, i) {
        if (e.get('name').toLowerCase().indexOf(lowerKey) > -1) {
          return true;
        } else if (e.get('url').toLowerCase().indexOf(lowerKey) > -1) {
          return true;
        } else if (_.contains(e.get('tag'), lowerKey)) {
          return true;
        }
      });
      if (temp.length) {
        this.models.length = 0;
        Array.prototype.push.apply(this.models, temp);
        this.trigger('reset');
      }
      return !!temp.length;
    },
    getMatch: function (fn) {
      return this.cache.filter(fn).slice(0);
    },
    restore: function () {
      this.models.length = 0;
      Array.prototype.push.apply(this.models, this.cache);
      this.trigger('reset');
    }
  });
  var FavoriteView = Backbone.View.extend({
    tagName: 'span',
    template: getTemp('singleFavorite.tmp'),
    events: {
      "dblclick figure": "remove"
    },
    initialize: function () {
      _.bindAll(this, 'render', 'remove');
      this.model.bind('change', this.render);
      this.model.bind('remove', this.remove);
    },
    render: function () {
      this.model.empty() ? this.$el.remove() : this.$el.html(this.template(this.model.toJSON()));
      return this;
    },
    remove: function () {
      this.model.clear();
    }
  });
  var AppView = Backbone.View.extend({
    el: '#body',
    events: {
      'keyup input': 'keyup'
    },
    favoriteCollection: new FavoriteCollection,
    initialize: function () {
      _.bindAll(this, 'fetch', 'renderAll', 'render', 'keyup', 'pop', 'result_for_modify', 'result_for_login');
      this.command = new command;
      this.$container = this.$el.find('#container');
      this.$el.on('click', '.pop-close', this.cls);// delegate handle for all pop-close
      this.favoriteCollection.bind('reset', this.renderAll);
      this.fetch();
    },
    fetch: function () {
      this.favoriteCollection.fetch({
        reset: this.renderAll,
        data: {_token: simpleStorage.get('_token')}
      });
    },
    renderAll: function () {
      this.$container.empty();
      this.$container.wqlScroll({wheel: 4});
      this.favoriteCollection.forEach(this.render);
    },
    render: function (favorite) {
      this.$container.append(new FavoriteView({model: favorite}).render().el);
    },
    pop: function (page, data) {
      $('.pop').remove();
      this.$el.append(getTemp(page || '404.tmp')(data || {}));
    },
    keyup: function (e) {
      var value = e.target.value.replace(/^\s+/, '').replace(/\s+$/, ''), param;
      switch (e.which) {
        case 13:// press enter key
          if (/^:\S+/.test(value)) {// special directive
            if (/close$/.test(value)) {
              this.cls();
            } else if ((param = value.substr(1).split(/ +/))[0] in this) {
              if (param.length > 1 && param[1].match(/^{/) && param[param.length - 1].match(/}$/)) {
                var json = '';
                try {
                  json = JSON.parse(param.slice(1).join(' '));
                } catch (e) {
                  this.pop('warn.tmp', {msg: 'JSON 格式不标准'});
                  return
                }
                this[param[0]].call(this, json);
              } else {
                this[param[0]].apply(this, param.slice(1));
              }
            } else {
              this.pop();
            }
          } else {// search directive
            this.favoriteCollection.search(e.target.value) ? this.cls() : this.pop();
          }
          this.command.add(e.target.value);
          this.$el.find('input').val('');
          break;
        case 38:// press up key
          this.$el.find('input').val(this.command.before());
          break;
        case 40:// press down key
          this.$el.find('input').val(this.command.next());
          break;
      }
    },
    help: function () {
      this.pop('help.tmp');
    },
    cls: function () {
      $('.pop-close').parent().remove();
    },
    reset: function () {
      this.favoriteCollection.restore();
    },
    add: function () {
      if (arguments.length < 1)
        return;
      var param = '';
      if (_.isObject(arguments[0])) {
        param = arguments[0];
      } else {
        var args = _.toArray(arguments);
        param = {
          name: args.shift(),
          url: args.shift(),
          tag: _.invoke(args, 'toLowerCase')
        };
      }
      new Favorite(param).callback(this.result_for_modify).save();
    },
    edit: function () {
      if (arguments.length < 1)
        return;
      var param = '';
      if (_.isObject(arguments[0])) {
        param = arguments[0];
      } else {
        var args = _.toArray(arguments);
        param = {
          id: args.shift(),
          name: args.shift(),
          url: args.shift(),
          tag: args.length ? _.invoke(args, 'toLowerCase') : null
        };
      }
      new Favorite(param).callback(this.result_for_modify).save();
    },
    remove: function (id) {
      if (arguments.length < 1)
        return;
      var param = _.isObject(arguments[0]) ? arguments[0] : {id: id};
      var url = '/save/' + simpleStorage.get('_token') + '/' + param.id;
      new Favorite(param).callback(this.result_for_modify).destroy({url: url});
    },
    reg: function (name, pwd) {
      new Reg().save(_.isObject(arguments[0]) ? arguments[0] : {name: name, pwd: pwd}, {
        success: this.result_for_login
      });
    },
    login: function (name, pwd) {
      new Login().save(_.isObject(arguments[0]) ? arguments[0] : {name: name, pwd: pwd}, {
        success: this.result_for_login
      });
    },
    loginout: function () {
      simpleStorage.deleteKey('_token');
      this.fetch();
    },
    result_for_modify: function (model, resp, options) {
      (resp && resp.code) ? this.fetch() : this.pop('warn.tmp', {msg: (resp && resp.msg || '请求失败，请重试')});
    },
    result_for_login: function (model, resp, options) {
      if (resp && resp.code && resp.msg) {
        simpleStorage.set('_token', resp.msg, {TTL: 86400000 * 7});// timeout is one week
        this.fetch();
      } else {
        this.pop('warn.tmp', {msg: (resp && resp.msg || '请求失败，请重试')});
      }
    },
    opentag: function (tag) {
      tag = tag || '';
      this.favoriteCollection.getMatch(function (e) {
        return _.contains(e.get('tag'), tag);
      }).forEach(function (e) {
        window.open(e.get('url'));
      });
    },
    open: function (id) {
      id = parseInt(id, 10);
      this.favoriteCollection.getMatch(function (e) {
        return e.get('id') === id;
      }).forEach(function (e) {
        window.open(e.get('url'));
      });
    }
  });
  window.XX = new AppView;
});